package com.xqh.study.netflixexampleorder.encrypt;

import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * @ClassName AESUtils
 * @Description AES加解密
 * @Author xuqianghui
 * @Date 2020/4/22 11:33
 * @Version 1.0
 */
public class AESUtils {


    public static void main(String[] args) throws UnsupportedEncodingException {
//        String text = "新闻|知晓天下事123ascdefg";//内容
//        String key = "b6khky7tx6stqpbc";//密码
//        String iv = "18o31yigkrp14459";//偏移量
//        String ecbResult = encryptToBase64(text, key, EncryptConstants.ALGORITHM_ECB_PKCS5);
//        String cbcResult = encryptToBase64(text, key, EncryptConstants.ALGORITHM_CBC_PKCS5, iv);
//
//        System.out.println("ECB加密结果: " + ecbResult);
//        System.out.println("CBC加密结果: " + cbcResult);
//
//        String ecbDecryptResult = decryptFromBase64(ecbResult, key, EncryptConstants.ALGORITHM_ECB_PKCS5);
//        String cbcDecryptResult = decryptFromBase64(cbcResult, key, EncryptConstants.ALGORITHM_CBC_PKCS5, iv);
//
//        System.out.println("ECB解密结果: " + ecbDecryptResult);
//        System.out.println("CBC解密结果: " + cbcDecryptResult);

        String str = "这里有几个汉字...";
        System.out.println(base64Encode(str.getBytes(EncryptConstants.CHAR_ENCODING)));
        String encodeStr = EncryptUtils.base64Encode(str);
        System.out.println(new String(base64Decode(encodeStr), "UTF-8"));
        System.out.println(EncryptUtils.base64Decode(encodeStr));

    }

    /**
     * 指定 加密类型
     *
     * @param data
     * @param key
     * @param algorithm
     * @return
     */
    public static byte[] encrypt(byte[] data, byte[] key, String algorithm, String iv) {
        if (key.length != 16) {
            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
        }
        return calcResult(data, key, Cipher.ENCRYPT_MODE, algorithm, iv);
    }

    public static byte[] decrypt(byte[] data, byte[] key, String algorithm, String iv) {
        if (key.length != 16) {
            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
        }
        return calcResult(data, key, Cipher.DECRYPT_MODE, algorithm, iv);
    }

    /**
     * 加/解密 计算结果
     *
     * @param data      内容
     * @param key       密码
     * @param model     加/解密
     * @param algorithm 算法类型
     * @param iv     偏移量  CBC模式 需要
     * @return
     */
    public static byte[] calcResult(byte[] data, byte[] key, int model, String algorithm, String iv) {
        try {
            SecretKeySpec secretKey = new SecretKeySpec(key, EncryptConstants.ENCRYPT_AES);
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec secKey = new SecretKeySpec(enCodeFormat, EncryptConstants.ENCRYPT_AES);
            Cipher cipher = Cipher.getInstance(algorithm);// 创建密码器
            if (StringUtils.hasText(iv) && algorithm.contains("/CBC/")) {
                //使用CBC模式，需要一个向量iv，可增加加密算法的强度
                IvParameterSpec ivPs = new IvParameterSpec(iv.getBytes(EncryptConstants.CHAR_ENCODING));
                cipher.init(model, secKey, ivPs);// 初始化
            }else {
                cipher.init(model, secKey);
            }
            byte[] result = cipher.doFinal(data);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("decrypt fail!", e);
        }
    }

    /**
     * 指定算法类型加密
     *
     * @param data
     * @param key
     * @param algorithm
     * @return
     */
    public static String encryptToBase64(String data, String key, String algorithm, String iv) {
        try {
            byte[] valueByte = encrypt(data.getBytes(EncryptConstants.CHAR_ENCODING), key.getBytes(EncryptConstants.CHAR_ENCODING), algorithm, iv);
            return base64Encode(valueByte);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("encrypt fail!", e);
        }
    }

    /**
     * 指定算法类型加密
     *
     * @param data
     * @param key
     * @param algorithm
     * @return
     */
    public static String encryptToBase64(String data, String key, String algorithm) {
        return encryptToBase64(data, key, algorithm, null);
    }

    /**
     * 指定 算法类型 解密
     *
     * @param data
     * @param key 密码 (utf-8格式)
     * @param algorithm
     * @param iv  偏移量
     * @return
     */
    public static String decryptFromBase64(String data, String key, String algorithm, String iv) {
        try {
            byte[] originalData = base64Decode(data);
            byte[] valueByte = decrypt(originalData, key.getBytes(EncryptConstants.CHAR_ENCODING), algorithm, iv);
            return new String(valueByte, EncryptConstants.CHAR_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("decrypt fail!", e);
        }
    }

    public static String decryptFromBase64(String data, String key, String algorithm) {
        return decryptFromBase64(data, key, algorithm, null);
    }

    /**
     * 解密
     * @param data
     * @param key 密码 (base64格式)
     * @param algorithm
     * @param iv
     * @return
     */
    public static String decryptWithKeyBase64(String data, String key, String algorithm, String iv) {
        try {
            byte[] originalData = base64Decode(data);
            byte[] valueByte = decrypt(originalData, base64Decode(key), algorithm, iv);
            return new String(valueByte, EncryptConstants.CHAR_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("decrypt fail!", e);
        }
    }

    public static String decryptWithKeyBase64(String data, String key, String algorithm){
        return decryptWithKeyBase64(data, key, algorithm, null);
    }

    public static byte[] generateRandomKey() {
        KeyGenerator keygen = null;
        try {
            keygen = KeyGenerator.getInstance(EncryptConstants.ALGORITHM_CBC_PKCS5);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(" generateRandomKey fail!", e);
        }
        SecureRandom random = new SecureRandom();
        keygen.init(random);
        Key key = keygen.generateKey();
        return key.getEncoded();
    }

    public static String generateRandomKeyWithBase64() {
        return base64Encode(generateRandomKey());
    }

    public static String base64Encode(byte[] data) {
        return java.util.Base64.getEncoder().encodeToString(data);
    }

    public static byte[] base64Decode(String key) {
        return java.util.Base64.getDecoder().decode(key);
    }

}
